package dao.jpa;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import domain.Page;

//@Transactional//让本类中的方法也开启事务 
public abstract class DaoSupport<T> implements Dao<T> {

	@PersistenceContext
	protected EntityManager em;
	protected Class<T> entityClass;//实体类class
	protected String entityName;//实体类名
	
	public DaoSupport(){
		entityClass=getEntityClass();
		entityName=getEntityName();
	}
	/**
	 * 获得实体类型
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected Class<T> getEntityClass(){
		//获得泛型的类型
		Type type=getClass().getGenericSuperclass();
		if(type instanceof ParameterizedType){
			ParameterizedType ptype=(ParameterizedType)type;
			return (Class<T>)ptype.getActualTypeArguments()[0];
		}
		return null;
	}
	/**
	 * 获得实体名称
	 * @return
	 */
	protected String getEntityName(){
		String name=null;
		Entity entity=this.entityClass.getAnnotation(Entity.class);
		if(entity==null || entity.name()==null || "".equals(entity.name())){
			name=entityClass.getSimpleName();
		}else{
			name=entity.name();
		}
		return name;
	}
	
	
	@Override
	public void remove(Serializable entityId){
		em.remove(get(entityId));
	}
	
	@Override
	public void removes(Object... ids) {
		StringBuilder sb=new StringBuilder("delete from ");
		sb.append(this.entityName).append(" where id in(");
		for (int i = 0; i < ids.length; i++) {
			if(i>0)sb.append(",");
			sb.append("?").append(i+1);
		}
		sb.append(")");
		this.execute(sb.toString(), ids);
	}
	
	
	@Override
	public  T get(Serializable entityId){
		return (T) em.find(entityClass, entityId);
	}

	@Override
	public  void save(T entity){
		em.persist(entity);
	}

	@Override
	public  void update(T entity){
		em.merge(entity);
	}
	
	
	
	@Override
	public  Long getCount(){
		String sql="select count(o) from "+this.entityName+" o";
		return (Long)em.createQuery(sql).getSingleResult();
	}
	
	/***
	 * 根据hql来查找
	 * @param hql
	 * @param param 参数
	 * @return	返回一个List的结果集
	 */
	@SuppressWarnings("unchecked")
	public List<T> findByHql(String hql,Object... param){
		Query query=em.createQuery(hql);
		for (int i = 0; param!=null && i < param.length; i++) {
			query.setParameter(i+1, param[i]);
		}
		return query.getResultList();
	}
	
	
	@Override
	public T getByHql(String jpql, Object... param) {
		List<T> result=findByHql(jpql, param);
		if(result!=null && result.size()>0){
			return result.get(0);
		}
		return null;
	}
	
	
	@Override
	public Object getFirstResult(String jpql, Object... param) {
		Query query=em.createQuery(jpql);
		for (int i = 0; param!=null && i < param.length; i++) {
			query.setParameter(i+1, param[i]);
		}
		return query.getSingleResult();
	}
	/***
	 * 获得所有数据
	 * @return 
	 */
	@Override
	public List<T> findAll(){
		String hql="select o from "+this.entityName+" o";
		return this.findByHql(hql, null);
	}
	
	/***
	 * 分页查询
	 * @param page 分页bean
	 * @return 指定页得数据
	 */
	@SuppressWarnings("unchecked")
	public Page<T> findByPage(Page<T> page){
		//总记录数
		String hql="select count(o) from "+this.entityName+" o ";
		if(!"".equals(page.getWhereSql()) && page.getWhereSql()!=null){
			hql+=" where "+page.getWhereSql();
		}
		page.setTotalRow((Long)getFirstResult(hql,page.getWhereParameter()));
		//构建查询hql语句
		hql=buildHql(page);
		Query query=em.createQuery(hql);
		// 2-1 * 2 =2
		// 2*2=4
		query.setFirstResult((page.getCurrentPage()-1)*page.getSize()).setMaxResults(page.getCurrentPage()*page.getSize());
		Object[] paramter=page.getWhereParameter();
		for (int i = 0; paramter!=null && i < paramter.length; i++) {
			query.setParameter(i+1, paramter[i]);
		}
		page.setResult(query.getResultList());
		//清空数据
		page.setWhereSql(null);
		page.setWhereParameter(null);
		page.setOrderItems(null);
		return page;
	}
	
	/***
	 * 构建hql语句
	 * @param page	
	 * @return	
	 */
	private String buildHql(Page<T> page){
		StringBuilder sb=new StringBuilder("select o from "+this.entityName+" o");
		if(page.getWhereSql()!=null && !"".equals(page.getWhereSql())){
			sb.append(" where ").append(page.getWhereSql());
		}
		if(page.getOrderItems()!=null && page.getOrderItems().size()>0){
			sb.append(" order by ");
			for (Entry<String, String> entry : page.getOrderItems().entrySet()) {
				sb.append("o.").append(entry.getKey()).append(" ").append(entry.getValue());
			}
		}
		return sb.toString();
	}
	
	@Override
	public void execute(String jpql, Object... param) {
		Query query=em.createQuery(jpql);
		for (int i = 0; param!=null &&  i < param.length; i++) {
			query.setParameter(i+1, param[i]);
		}
		query.executeUpdate();
	}
	
	/***
	 * 更新表数据
	 * @param datas 字段名与数据的键值对
	 * @param whereSql where条件，不带where,且占位的索引应接着datas的个数一直累加，请切记
	 * @param whereParams 与where条件相对应的值
	 */
	@Override
	public void updateByHql(Map<String, Object> datas,String whereSql,Object... whereParams) {
		StringBuilder sb=new StringBuilder("update ");
		sb.append(this.entityName).append(" set ");
		List<Object> params=new ArrayList<>();
		for (Entry<String, Object> entry : datas.entrySet()) {
			if(params.size()>0) sb.append(",");
			params.add(entry.getValue());
			sb.append(entry.getKey()).append("=?").append(params.size());
		}
		if(whereSql!=null && !"".equals(whereSql)){
			sb.append(" where ").append(whereSql);
			//把where参数附加到后面
			params.addAll(Arrays.asList(whereParams));
			this.execute(sb.toString(), params.toArray());
		}else{
			this.execute(sb.toString(), params.toArray());
		}
	}
	
	/****
	 * 根据id来更新表数据
	 * @param datas
	 * @param id
	 */
	@Override
	public void updateById(Map<String, Object> datas,Serializable id){
		String whereSql=" id=?"+(datas.size()+1);
		this.updateByHql(datas, whereSql, id);
	}

}
